package controller

import (
	"errors"
	"reflect"
	"strings"
	"time"

	"gddgame.cc/galaxy/binocle"
	. "gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game/achievement"
	. "gddgame.cc/galaxy/common/dsl/game/base"
	. "gddgame.cc/galaxy/common/dsl/game/config"
	. "gddgame.cc/galaxy/common/dsl/game/group"
	. "gddgame.cc/galaxy/common/dsl/game/reward"
	. "gddgame.cc/galaxy/common/dsl/game/stat"
	. "gddgame.cc/galaxy/common/dsl/game/user"
	. "gddgame.cc/galaxy/common/dsl/web"
	"gddgame.cc/galaxy/common/namespaces"
	"gddgame.cc/galaxy/common/service/playbook"
	"gddgame.cc/galaxy/common/service/playbook/game_user"
	"gddgame.cc/galaxy/core/sdk"
	"gddgame.cc/galaxy/project/maze/maze/game"
	"gddgame.cc/galaxy/project/maze/maze/model"
	"gddgame.cc/galaxy/project/maze/maze/model/player_model"
	"gddgame.cc/galaxy/project/maze/maze/model/player_single"
	"gddgame.cc/galaxy/server/composer"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/orm"
)

var (
	Center *game.Center

	Env *Environment
)

func bindData(context def.Context, center *game.Center) error {
	var err error

	// config
	if _, err := GetConfigData(Env, model.InitConfig); err != nil {
		return err
	}

	// stat
	if center.StatData, err = GetStatData(Env); err != nil {
		return err
	}

	// group
	if center.GroupData, err = GetGroupData(Env); err != nil {
		return err
	}

	// user
	if center.UserData, err = GetUserData(Env); err != nil {
		return nil
	}

	if center.AchievementData, err = GetAchievementData(Env, reflect.TypeOf(&Reward{}), nil); err != nil {
		return err
	}

	if center.BaseData, err = GetBaseData(Env); err != nil {
		return err
	}

	// player
	if center.PlayerModelData, err = player_model.GetPlayerModelData(Env); err != nil {
		return err
	}
	if center.PlayerSingleData, err = player_single.GetPlayerSingleData(Env); err != nil {
		return err
	}

	// center
	center.Compute()

	return nil
}

func InitGame(builder composer.Builder, app binocle.App, client *sdk.AppClient) error {
	builder.Infof("Init Game")
	Env = NewEnvironment(builder, app.Name(), client, builder.Cache(app.FullName()))
	Env.SetApp(app)
	if err := Env.RegisterModule(player_model.Provider); err != nil {
		return err
	}
	if err := Env.RegisterModule(player_single.Provider); err != nil {
		return err
	}
	if err := Env.RegisterModule(model.Provider); err != nil {
		return err
	}
	Env.RegisterInit(func(env *Environment) error {
		c := game.NewCenter(Env)

		if err := bindData(builder, c); err != nil {
			return err
		}
		Center = c
		return nil
	})
	return Env.Start()
}

func StartPlaybook(client *sdk.AppClient, app binocle.App, instance *namespaces.RoomInstance) (*playbook.Playbook, error) {
	// 获取用户信息
	token := instance.Token()
	users := instance.Members()
	openIds := make([]string, 0, len(users))
	for key, _ := range users {
		openIds = append(openIds, key)
	}
	var scene playbook.Scene
	var gt GameType
	setting := instance.Setting()
	mapId := setting["mapId"].(string)
	manager := game_user.NewPlayerManager(Env, Center.UserData, Center)
	if strings.HasPrefix(mapId, "speed") {
		scene = game.NewSpeedScene(Center, manager)
		gt = model.SpeedGame
	} else if strings.HasPrefix(mapId, "find") {
		scene = game.NewFindScene(Center, manager)
		gt = model.FindGame
	} else {
		return nil, errors.New("scene is error")
	}
	var start time.Time
	pb := playbook.NewPlaybook(token, scene, openIds, app)
	scene.Bind(pb)

	if len(openIds) == 1 {
		//var u model.User
		//if err := Center.RandomUser(&u, []string{"is_system = 1"}); err != nil {
		//	return nil, err
		//}
		//pb.JoinAi(u.OpenId)
		pb.JoinAi("")
	}
	pb.OnStart(func(players []*playbook.Player) {
		// 记录用户战斗进度
		start = time.Now()
		for _, player := range players {
			if player.Ai {
				continue
			}
			_ = Env.UpdateUserStatus(player.OpenId, RoomKey, token)
		}
	})
	pb.OnExit(func(player *playbook.Player) {
		_ = Env.UpdateUserStatus(player.OpenId, RoomKey, "")
		// 记录最新信息
		var user model.User
		_ = Center.FindUser(player.OpenId, &user)

		if err := manager.UpdateUserStats(client, player, &user, Center.StatData); err != nil {
			client.Error(err)
		}
		//data := make(map[string]interface{})
		//if err := Center.UpdateUser(&user, data); err != nil {
		//	client.Error(err)
		//}
	})
	pb.OnFinish(func(players []*playbook.Player) {
		people := len(players)
		members := make(map[string]interface{})

		for _, player := range players {
			if player.Ai {
				continue
			}
			_ = Env.UpdateUserStatus(player.OpenId, RoomKey, "")
			if player.Exit {
				continue
			}
			// 记录最新信息
			var user model.User
			_ = Center.FindUser(player.OpenId, &user)

			result := player.Result.(*model.GameResult)

			message, err := SendOnlineMessage(client, user.Id, mapId, RewardFromGoldReward(result.Reward))
			if err != nil {
				client.Error(err)
			}
			result.Result = NewUserMessage(message, player.Language, model.MessageTemplates)

			data := make(map[string]interface{})
			userGame, _ := FinishGame(client, people, gt, &start, player, &user, data)

			if err := manager.UpdateUserStats(client, player, &user, Center.StatData); err != nil {
				client.Error(err)
			}
			if err := Center.UpdateUser(&user, data); err != nil {
				client.Error(err)
			}
			members[player.OpenId] = userGame.Id
		}
		// 传递记录id
		instance.OnFinish(members)
	})

	return pb, nil
}

func FinishGame(context def.Context, people int, gt GameType, startTime *time.Time, player *playbook.Player, user *model.User, data map[string]interface{}) (*UserGame, error) {
	var err error
	result := player.Result.(*model.GameResult)
	userGame := UserGame{
		UserId:    user.Id,
		Type:      gt,
		People:    people,
		Result:    *result,
		StartTime: startTime,
	}
	if result.Score > 0 {
		data["online_score"] = &orm.UpdateOperate{Operate: "+", Value: result.Score}
		_ = Env.UpdateLeaderboard(Env.ID(), user.OpenId, model.OnlineLeaderboard, user.OnlineScore+result.Score)
	} else if result.Score < 0 && user.OnlineScore > 0 {
		_ = Env.UpdateLeaderboard(Env.ID(), user.OpenId, model.OnlineLeaderboard, user.OnlineScore-result.Score)
		data["online_score"] = &orm.UpdateOperate{Operate: "-", Value: result.Score * -1}
	}
	if err := Center.InsertUserGame(&userGame, 1800); err != nil {
		goto Rollback
	}
	return &userGame, nil
Rollback:
	return nil, SystemExceptionDetail("更新用户战斗记录失败", err)
}
